package com.dr.cmdb.core.aop;



import com.dr.cmdb.common.log.LoggerUtil;
import com.dr.cmdb.common.model.errorcode.BaseCode;
import com.dr.cmdb.common.model.response.ApiResponse;
import com.dr.cmdb.core.annotation.RedisLock;
import com.dr.cmdb.core.errorcode.BizCode;
import com.dr.cmdb.core.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @ProjectName: cmdb
 * @Package: com.dr.cmdb.application.aop
 * @Description: java类作用描述
 * @Author: duanrui
 * @CreateDate: 2021/4/17 21:36
 * @Version: 1.0
 * <p>
 * Copyright: Copyright (c) 2021
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class RedisLockAspect {


    @Autowired
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.dr.cmdb.core.annotation.RedisLock)")
    public void redisLockAspectAspect() {
    }

    @Around("redisLockAspectAspect()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //拿到被注解的方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //拿到被注解的方法
        Method method = signature.getMethod();
        Method targetMethod = AopUtils.getMostSpecificMethod(method, joinPoint.getTarget().getClass());
        //拿到lock参数
        RedisLock redisLock = AnnotationUtils.findAnnotation(targetMethod, RedisLock.class);
        if (redisLock == null) {
            return new ApiResponse().error(BaseCode.REDIS_FRAME_ERROR);
        }
        RLock lock = null;
        try {
            lock = redissonClient.getLock(redisLock.lockKey());
            //尝试加锁，自动续锁功能开启，上锁以后10秒自动解锁
            if (lock.tryLock(10, TimeUnit.SECONDS)) {
                return joinPoint.proceed();
            }
        } catch (Throwable e) {
            if (e instanceof InterruptedException) {
                e = new BizException(BizCode.REDIS_LOCK_FAILED);
                LoggerUtil.error(log, "redis加锁失败{}", redisLock.lockKey(), e);
            }
            throw e;
        } finally {
            //解锁
            if (null != lock) {
                lock.unlock();
            }
        }
        return null;
    }


}
